package com.billy.ohos.swipe;

import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;

/**
 * A smart swipe util to wrap a view and consume swipe event to do some business via {@link SwipeConsumer}
 * classic usage:
 * <pre>
 *     SmartSwipe.wrap(view)    //specific the view to wrap
 *          .addConsumer(new StretchConsumer()) // add consumer to consume swipe event
 *          .enableVertical(); //enable consumer`s direction(s)
 * </pre>
 *
 * @author billy.qi
 */
public class SmartSwipe {

    /**
     * wrap an ability
     * the content view is: ohos.R.id.content
     *
     * @param ability ability  根布局
     * @return the wrapper
     */
    public static SmartSwipeWrapper wrap(Component ability) {
        SmartSwipeWrapper wrapper = peekWrapperFor(ability);
        if (wrapper != null) {
            return wrapper;
        }
        if (ability instanceof ComponentContainer) {
            ComponentContainer group = (ComponentContainer) ability;
            int childCount = group.getChildCount();
            for (int i = 0; i < childCount; i++) {
                Component child = group.getComponentAt(i);
                if (child.findComponentById(ConstantId.ABLITY_CONTENT_ID) != null) {
                    return wrapView(child);
                }
            }
        }
        return wrapView(ability);
    }

    /**
     * peek wrapper for the specific ability, return the origin {@link SmartSwipeWrapper} if exists, else return null
     *
     * @param contentView Component  根布局
     * @return the wrapper if exists, otherwise returns null
     */
    public static SmartSwipeWrapper peekWrapperFor(Component contentView) {
        while (contentView != null) {
            if (contentView.getComponentParent() instanceof SmartSwipeWrapper) {
                return (SmartSwipeWrapper) contentView.getComponentParent();
            }
            contentView = (Component) contentView.getComponentParent();
        }
        return null;
    }

    /**
     * wrap a view in ability, view id is specified
     * if already wrapped, returns the original wrapper
     *
     * @param ability ability
     * @param viewId  the id of view to be wrapped
     * @return the original wrapper or create a new wrapper to wrap the view and replace its place into parent
     */
    public static SmartSwipeWrapper wrap(Component ability, int viewId) {
        if (ability != null) {
            Component view = ability.findComponentById(viewId);
            if (view != null) {
                return wrapView(view);
            }
        }
        return null;
    }

    /**
     * wrap a view
     *
     * @param view the view to be wrapped
     * @return the original wrapper or create a new wrapper to wrap the view and replace its place into parent
     */
    public static SmartSwipeWrapper wrapView(Component view) {
        SmartSwipeWrapper wrapper = peekWrapperForView(view);
        if (wrapper != null) {
            return wrapper;
        }
        ComponentContainer.LayoutConfig layoutParams = view.getLayoutConfig();
        if (view.getComponentParent() != null) {
            ComponentContainer viewParent = (ComponentContainer) view.getComponentParent();
            wrapper = createNewWrapper(view.getContext());
            int index = viewParent.getChildIndex(view);
            viewParent.removeComponent(view);
            viewParent.addComponent(wrapper, index, layoutParams);
        } else {
            wrapper = createNewWrapper(view.getContext());
            if (layoutParams != null) {
                int height = measureHeight(view);
                if (height > 0) {
                    layoutParams.height = height;
                }
                wrapper.setLayoutConfig(layoutParams);
            }
        }
        wrapper.setContentView(view);
        return wrapper;
    }

    private static int measureHeight(Component view) {
        int height = 0;
        if (view instanceof DirectionalLayout) {
            height = recursionMeasureHeight(view, 0);
        }
        return height;
    }

    private static int recursionMeasureHeight(Component view, int height) {
        if (view instanceof DirectionalLayout) {
            DirectionalLayout directionalLayout = ((DirectionalLayout) view);
            int childCount = directionalLayout.getChildCount();
            for (int i = 0; i < childCount; i++) {
                Component child = directionalLayout.getComponentAt(i);
                int all = child.getHeight() + child.getPaddingTop() + child.getPaddingBottom()
                        + child.getMarginTop() + child.getMarginBottom();
                height = height + all;
                height = height + recursionMeasureHeight(child, height);
            }
        }
        return height;
    }

    /**
     * get wrapper of the specific view
     *
     * @param view view to find wrapper
     * @return the original wrapper of the specific view
     */
    public static SmartSwipeWrapper peekWrapperForView(Component view) {
        if (view != null) {
            if (view.getComponentParent() instanceof SmartSwipeWrapper) {
                return (SmartSwipeWrapper) view.getComponentParent();
            }
        }
        return null;
    }

    /**
     * switch direction enable for all {@link SwipeConsumer} that already added to the wrapper
     *
     * @param view      the view which be wrapped
     * @param enable    true: to enable, false: to disable
     * @param direction direction to enable or disable
     */
    public static void switchDirectionEnable(Component view, boolean enable, int direction) {
        enableOrDisableFor(peekWrapperFor(view), enable, direction);
    }

    public static void switchDirectionEnableView(Component ability, boolean enable, int direction) {
        enableOrDisableFor(peekWrapperFor(ability), enable, direction);
    }

    private static void enableOrDisableFor(SmartSwipeWrapper wrapper, boolean enable, int direction) {
        if (wrapper != null) {
            wrapper.enableDirection(direction, enable);
        }
    }

    public static int dp2px(int dp, Context context) {
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        final float scale = display.getAttributes().densityPixels;
        return (int) (dp * scale + 0.5f);
    }

    public static int ensureBetween(int origin, int min, int max) {
        return Math.max(min, Math.min(origin, max));
    }

    public static float ensureBetween(float origin, float min, float max) {
        return Math.max(min, Math.min(origin, max));
    }

    public static double ensureBetween(double origin, double min, double max) {
        return Math.max(min, Math.min(origin, max));
    }

    private static IWrapperFactory factory;

    private static SmartSwipeWrapper createNewWrapper(Context context) {
        final IWrapperFactory factory = SmartSwipe.factory;
        if (factory != null) {
            SmartSwipeWrapper wrapper = factory.createWrapper(context);
            if (wrapper != null) {
                return wrapper;
            }
        }
        return new SmartSwipeWrapper(context);
    }

    /**
     * set the factory of {@link SmartSwipeWrapper}
     * by default, create the {@link SmartSwipeWrapper} instance to wrap if {@link #factory} is null
     *
     * @param factory the factory
     * @see #createNewWrapper(Context)
     */
    public static void setFactory(IWrapperFactory factory) {
        SmartSwipe.factory = factory;
    }

    public interface IWrapperFactory {
        SmartSwipeWrapper createWrapper(Context context);
    }

    private static boolean initFactoryByClassName(String factoryClassName) {
        Class<?> clazz;
        try {
            clazz = Class.forName(factoryClassName);
            if (clazz != null) {
                Object object = clazz.getConstructor().newInstance();
                if (object instanceof IWrapperFactory) {
                    setFactory((IWrapperFactory) object);
                }
            }
            return true;
        } catch (Exception ignored) {
            ignored.printStackTrace();
        }
        return false;
    }
}
